var express = require('express');
var app = express();
var url = require('url');
var path = require('path');
const spawn = require('child_process').spawn;
const fs = require('fs');
const readline = require('readline');

var bodyParser = require('body-parser')
app.use( bodyParser.json() );       // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
  extended: true
})); 

//app.use(express.json());       // to support JSON-encoded bodies
//app.use(express.urlencoded()); // to support URL-encoded bodies

/*routes*/
var applications=[];
var NoCConfig;
var status;

app.get('/',function(req,res){
	var url_parts = url.parse(req.url, true);
	var query = url_parts.query;
	res.sendFile(path.join(__dirname + '/views/index.html'));
});
app.get('/font.js', function(req, res) {
    res.sendfile(__dirname + '/views/font.js');  
});

app.post('/generates',function(req,res){
	applications = req.body;
	var applicationFilePrefix = "..\\simulate\\app\\app";
    for (applicationIndex in applications) {
        var application = applications[applicationIndex];
		 fs.writeFileSync(applicationFilePrefix+application.id+".txt",application.id+"\t");
		 fs.writeFileSync(applicationFilePrefix+application.id+".txt",application.arrivalTime+"\t",{'flag':'a'});
		 fs.writeFileSync(applicationFilePrefix+application.id+".txt",application.taskNum+"\t",{'flag':'a'});
		for(taskIndex in application.tasks){
			var applicationStream ="";
			var task = application.tasks[taskIndex];
			applicationStream += (task.id+"\t" + task.execution+"\t");
			for(neiborIndex in task.neibors){
				applicationStream += (task.neibors[neiborIndex]+"\t")
			}
			fs.writeFileSync(applicationFilePrefix+application.id+".txt",applicationStream+"\t",{'flag':'a'});
		}
		
	}
	res.end("generated");
});
app.post('/builds',function(req,res){
	//console.log(JSON.parse(req.body));
	NoCConfig = req.body;
    console.log(NoCConfig);
	res.end("built");
});

var WebSocketServer = require('ws').Server, wss = new WebSocketServer({port: 8080});
app.post('/simulates',function(req,res){
	//console.log(NoCConfig.width+""+applications.length);
	var simulate = spawn("..\\simulate\\Simulator\\x64\\Debug\\Simulator.exe",[NoCConfig.width+"",applications.length+""]);
   //var simulate = spawn("..\\simulate\\Simulator\\x64\\Debug\\Simulator.exe", ["8", "6"]);
    simulate.stderr.on('data', (data) => {
        console.log(`stderr: ${data}`);
    });
    simulate.on('close', (code) => {
            console.log(`child process exited with code ${code}`);
    });
    wss.on('connection', function (ws) {
        console.log("ws connected!");
        readline.createInterface({
            input     : simulate.stdout,
            terminal  : false
        }).on('line', function (line) {
            //console.log("line");
            var hotspot = spawn("..\\simulate\\Simulator\\x64\\Release\\HotSpot.exe",['-c','..\\simulate\\HotSpot\\hotspot.config','-f','..\\simulate\\app\\power.flp','-p','..\\simulate\\app\\power.ptrace','-o','..\\simulate\\app\\power.ttrace']);
        
        hotspot.on('close', (code) => {
           // var fileReadStream = fs.createReadStream("..\\simulate\\app\\power.ttrace");
            
        });
            var message = {
                    taskProfile: line
                }
            console.log(message.taskProfile);
            ws.send(JSON.stringify(message));
        });
    });
	res.end();
})

app.listen(1234,function(){
	console.log("client in!");
});



/*
var WebSocketServer = require('ws').Server, wss = new WebSocketServer({port: 8080});

	wss.on('connection', function(ws) {
		function mapping(application,NoC){
			var mapped = true;
			for(taskIndex in application.tasks){
				var nextCore = 0;
				loopl:
				for(l in NoC ){
					for(h in NoC[l]){
						for(w in NoC[l][h]){
							if(NoC[l][h][w].status == "off"){
								nextCore = [l,h,w];
								break loopl;
							}
						}
					}
				}
				if(nextCore == 0){
					mapped = false;
					return {mapped:mapped,application:application,NoC:NoC};
				}else{
					
					NoC[l][h][w].status = "on";
					NoC[l][h][w]["runningTask"] = [application.id,taskIndex];
					application.tasks[taskIndex]["mappedCore"] = nextCore;
					nextCore = undefined;	
				}
			}
			
			return {mapped:mapped,application:application,NoC:NoC};
		}
		function oneSecond(app,NoC){
			var finished = true;
			for(taskIndex in app.tasks){
				var mapped = app.tasks[taskIndex]["mappedCore"];
				var mappedCore = NoC[mapped[0]][mapped[1]][mapped[2]];
				app.tasks[taskIndex].executionVolume -= mappedCore.executionSpeed;
				if(app.tasks[taskIndex].executionVolume <= 0){
					app.tasks[taskIndex].executionVolume  = 0;
				}else{
					finished = false;
				}
			}
			if(finished){
				app["status"] = "finished";
			}
			return app;
		}

		
	    var clock = 0;
	    var waiting = [];
	    var running = [];
	    var finished = [];
	    var applicationSet = applications[user]
	    
	    while(waiting.length != 0 || running.length != 0 || applicationSet.length != 0){
	    	//console.log(applications[user]);
	    	//console.log("clock");
	    	//console.log(clock);
	    	for(var applicationIndex = 0;applicationIndex < applicationSet.length;applicationIndex++){
	    		var appArrive = applicationSet[applicationIndex];
	    		if(appArrive.arrivalTime == clock){
	    			appArrive["waittingTime"] = 0;
	    			var mappingResult = mapping(appArrive,NoC);
	    			if(mappingResult.mapped){
	    				appArrive["status"] = "running";
	    				appArrive = mappingResult.application;
	    				NoC = mappingResult.NoC;
	    				running.push(appArrive);
	    			}else{
	    				appArrive["status"] = "waiting";
	    				waiting.push(appArrive);
	    			}
	    			applicationSet.splice(applicationIndex,1); //改变下表
	    			applicationIndex--;
	    		}
	    	}
	    	for(var waitingIndex = 0;waitingIndex<waiting.length;waitingIndex++){
	    		var appWaiting = waiting[waitingIndex];
	    		var mappingResult = mapping(appWaiting,NoC);
	    		if(mappingResult.mapped){
	    			appWaiting["startRunningTime"] = clock;
	    			appWaiting["status"] = "running"
	    			running.push(appWaiting);
	    			waiting.splice(waitingIndex,1);
	    			waitingIndex--;
	    		}else{
	    			appWaiting["waittingTime"] +=1;
	    			waiting[waitingIndex] = appWaiting;
	    		}
	    	}

	    	for(var runningIndex = 0;runningIndex<running.length;runningIndex++){
	    		running[runningIndex] = oneSecond(running[runningIndex],NoC);
	    		if(running[runningIndex].status == "finished"){
	    			finished.push(running[runningIndex]);
	    			running.splice(runningIndex,1);
	    			runningIndex--;
	    		}
	    	}
	    	
	    	
	    	clock+=1;
	    	console.log(clock);
	    	var applicationStatus = {waiting:waiting,running:running,finished:finished};
	    	var map = [];
	    	var thermalProfile = [];
	    	for(l in NoC){
	    		var hMap=[];
	    		var hTP=[];
				for(h in NoC[l]){
					var wMap = [];
					var wTP = [];
					for(w in NoC[l][h]){
						//wTP.push(78);
						if(NoC[l][h][w].status == "on"){
							wMap.push(NoC[l][h][w]["runningTask"])
							NoC[l][h][w].temperature += 5;
						}else{
							wMap.push(undefined);
							NoC[l][h][w].temperature -= 8;
						}
						wTP.push(NoC[l][h][w].temperature);
					}
					hMap.push(wMap);
					hTP.push(wTP);
				}
				map.push(hMap);
				thermalProfile.push(hTP);
			}
			debugger;
	    	ws.send(JSON.stringify({applicationStatus:applicationStatus,map:map,thermalProfile:thermalProfile}));
	    }
	});
	



function buildNoC(NoCConfig){
	var NoC = [];
	for(var l=0;l<NoCConfig.length;l++){
		var heightLine=[];
		for(var h=0;h<NoCConfig.height;h++){
			var widthLine = [];
			for(var w=0;w<NoCConfig.width;w++){
					var core = {
						vertex:[l,h,w],
						executionSpeed:NoCConfig.executionSpeed,
						communicationSpeed:NoCConfig.communicationSpeed,
						temperature:NoCConfig.temperatureInit,
						status:"off"
					}
					widthLine.push(core);
			}
			heightLine.push(widthLine);
		}
		NoC.push(heightLine);
	}
	return NoC;
}
*/